/**
 * Copyright [2010] Delta Air Lines, Inc. All Rights Reserved Access,
 * Modification, or Use Prohibited Without Express Permission of Delta Air
 * Lines.
 */
package com.delta.rafg.core.log;


import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import com.delta.rafg.core.componentbase.exception.DLException;
import com.delta.rafg.coredata.AObject;
import com.delta.rafg.coredata.ClientData;


/**
 * @author RAFG Development Team (D9785692)
 */
public class LogData extends AObject {

    /**
     * serialVersion property.
     */
    private static final long serialVersionUID = 3069023150271832399L;

    /**
     * clientData property.
     */
    private ClientData clientData;

    /**
     * applicationData property.
     */
    private Object applicationData;

    /**
     * otherObjects property.
     */
    private List<Object> otherObjects;

    /**
     * className property.
     */
    private String className;

    /**
     * fullClassName property.
     */
    private String fullClassName;

    /**
     * methodName property.
     */
    private String methodName;

    /**
     * codeLocation property.
     */
    private String codeLocation;

    /**
     * freeText property.
     */
    private String freeText;

    /**
     * exception property.
     */
    private Throwable exception;

    /**
     * timeStamp property.
     */
    private long timeStamp;

    /**
     * severity property.
     */
    private int severity = INFO;

    /**
     * request property.
     */
    private Serializable request;

    /**
     * response property.
     */
    private Serializable response;

    public static final int FATAL = 1;

    public static final int ERROR_ONLY = 2; 

    public static final int ERROR = ERROR_ONLY + FATAL;
    
    public static final int STATS_ONLY = 4;
    
    public static final int STATS = STATS_ONLY + ERROR;

    public static final int WARNING = 8 + STATS;

    public  static final int INFO = 16 + WARNING;

    public static final int ALL = INFO;

    public static final int OFF = 0;

    /**
     * loggerClassName property.
     */
    private static String loggerClassName = RAFGLogger.class.getName();

    /**
     * commonExClsName property.
     */
    private static List<String> commonExClsName = new ArrayList<String>(2);
    
    static{
    	addCommonExceptionClsName(DLException.class.getName());
    	addCommonExceptionClsName("com.delta.rafg.core.base.AWorkerBean");
    	addCommonExceptionClsName("com.delta.rafg.core.base.ABackendInterface");
    	addCommonExceptionClsName("com.delta.rafg.core.base.AServiceBean");
    }

    /**
     * logDtFormat property.
     */
    private static DateFormat logDtFormat = new SimpleDateFormat(
        "ddMMMyyyy hh:mm:ss.SSS");

    /**
     * dateformat property.
     */
    private static DateFormat diffDtFormat = new SimpleDateFormat("mm:ss.SSS");

    /**
     * dtTimeStamp property.
     */
    private Date dtTimeStamp = null;

    /**
     * dtDiffTimeStamp property.
     */
    private Date dtDiffTimeStamp = null;

    /**
     * timeStampDiff property.
     */
    private long timeStampDiff = 0;
    
    /**
     * Fields with one to one mapping with the database
     * for storing log data in the database.
     * 
     */
    
    private String userPermission = null;
    private String logEntryTypeCode = null;
    private long errorMsgNo = 0L;  
    private String errorMessage = null;
    private String bkndInterfaceTypCode = null;
    private String bkndEnvironmentName = null;
    private String bkndServiceName = null;
    private String bkndSessionIdentifier = null;
    private String bkndActionName = null;
    private String bkndLocIdentifier = null;
    private long bkngCreationDate = 0L;
    private String tktDocNo = null; 
    private long tktDocIssueLocalDate = 0L;
    private String mktCarrierCode = null;
    private String mktFlightNo = null;  
    private String bkngClassOfServiceCode = null;
    private String opAsCarrierCode = null;
    private String opAsFlightNumber = null;
    private long schldSegDeptlocalDate = 0L;
    private String orgTrnsprtStnCode = null;
    private String destTrnsprtStnCode = null;
    private String custIdentifier = null;
    private String mbrPrgTierCode = null;
    private String custTypeCode = null;
    private String appClientIdentifier = null;
    private String clientAppDetail = null;
    private Object backEndRequest = null;
    private Object backEndResponse = null;
    private Object addtionalObjects = null;
    private Object userSettings = null;
    private Object applicationSettings = null;
    private boolean logged = true; 

    /**
     * LogData Constructor.
     * @param pRequest request
     * @param pResponse response
     * @param pOtherObjects otherobjects
     */
    public LogData(final Serializable pRequest, final Serializable pResponse,
        final Object... pOtherObjects) {

        this(null, pRequest, pResponse, pOtherObjects);
    }

    /**
     * LogData Constructor.
     * @param pFreeText freetext
     * @param pRequest request
     * @param pResponse response
     * @param pOtherObjects otherobjects
     */
    public LogData(final boolean pIsLogged, final String pFreeText, final Serializable pRequest,
        final Serializable pResponse, final Object... pOtherObjects) {

        this(pFreeText, null, pRequest, pResponse, pOtherObjects);
        setLogged(pIsLogged);
    }

    /**
     * LogData Constructor.
     * @param pFreeText freetext
     * @param pRequest request
     * @param pResponse response
     * @param pOtherObjects otherobjects
     */
    public LogData(final String pFreeText, final Serializable pRequest,
        final Serializable pResponse, final Object... pOtherObjects) {

        this(pFreeText, null, pRequest, pResponse, pOtherObjects);
    }

    /**
     * LogData Constructor.
     * @param pFreeText freetext
     * @param pException exception
     * @param pRequest request
     * @param pResponse response
     * @param pOtherObjects otherobjects
     */
    public LogData(final String pFreeText, final Throwable pException,
        final Serializable pRequest, final Serializable pResponse,
        final Object... pOtherObjects) {

        StackTraceElement theStack = null;
        if(pException == null)
        	theStack = determineStack();
        else
        	theStack = determineStack(pException);
        
        setRequest(pRequest);
        setResponse(pResponse);
        setOtherObjects(pOtherObjects);
        setException(pException);
        if( theStack != null ){
	        setFullClassName(theStack.getClassName());
	        setClassName(theStack.getFileName());
	        if( getClassName() != null && getClassName().length() > 5)
	        	setClassName(getClassName().substring(0, getClassName().length() - 5));
	        setMethodName(theStack.getMethodName());
	        setCodeLocation(theStack.getFileName() + ":" + theStack.getLineNumber());
        }
        setFreeText(pFreeText);
        setTimeStamp();
    }

    /**
     * captureTimeTaken Constructor.
     * @param pLogData logdata
     */
    public final void captureTimeTaken(final LogData pLogData) {

        if (pLogData != null) {
            timeStampDiff = getTimeStamp() - pLogData.getTimeStamp();
            dtDiffTimeStamp = new Date(timeStampDiff);
        }
    }


    /**
     * Identifies the location where this was called.
     * @return String.
     */
	static String determineCodeLocation() {
		try{
			StackTraceElement stE = determineStack();
			return stE.getFileName() + ":" + stE.getLineNumber() 
				+ "(" + stE.getClassName() + "." + stE.getMethodName() + ")";
		}
		catch(Throwable t){
			return "";
		}
    }
	
    /**
     * Identifies the class and method that invoked the logger.
     * @return determineStack.
     */
	private static StackTraceElement determineStack() {

        try {
            throw new Exception();
        } catch (final Exception e) {
            return determineStack(e);
        }
    }

    /**
     * Identifies the class and method created the exception, which invoked the
     * logger.
     * @return determineStack.
     * @param e e
     */
    private static StackTraceElement determineStack(final Throwable e) {

        if (e != null) {
            final StackTraceElement[] stkTrace = e.getStackTrace();
            for (final StackTraceElement element : stkTrace) {
                final String callingClassName = element.getClassName();
                if (!callingClassName.equals(loggerClassName)
                    && !commonExClsName.contains(callingClassName)
                    && !callingClassName.equals(LogData.class.getName())
                    && !callingClassName.equals(e.getClass().getName())
                    && callingClassName.startsWith("com.delta") ) { 
                    return element; }
            }
        }
        return new Exception().getStackTrace()[0];
    }

    @Override
    public final boolean equals(final Object pLogData) {

        if (pLogData == null && !(pLogData instanceof LogData)) { return false; }
        final LogData that = (LogData) pLogData;
        return getRequest() == that.getRequest()
            && getResponse() == that.getResponse()
            && (getOtherObjects() == that.getOtherObjects() 
            	|| (getOtherObjects() != null && getOtherObjects().equals(that.getOtherObjects())));
    }

    /**
     * Returns applicationData.
     * @return getApplicationData
     */
    public final Object getApplicationData() {

        if (applicationData == null) {
            return getRequest();
        } else {
            return applicationData;
        }
    }

    /**
     * @return the className
     */
    public final String getClassName() {

        return className;
    }

    /**
     * @return the clientData
     */
    public final ClientData getClientData() {
		if (clientData == null) {
    		clientData = new ClientData();
    		clientData.setBusinessProcess("Not.SvcBean." + getClassName());
    		clientData.setClientSessionId("NoClientData");
    	}
		else{
			if(clientData.getBusinessProcess() == null 
					|| clientData.getBusinessProcess().isEmpty()){
	    		clientData.setBusinessProcess("Not.SvcBean." + getClassName());
			}
			if(clientData.getClientSessionId() == null 
					|| clientData.getClientSessionId().isEmpty()){
	    		clientData.setClientSessionId("NoClientData");
			}
		}
        return clientData;
    }

    /**
     * @return the codeLocation
     */
    public final String getCodeLocation() {

        return codeLocation;
    }

    /**
     * @return the exception
     */
    public final Throwable getException() {

        return exception;
    }

    /**
     * @return the freeText
     */
    public final String getFreeText() {

        return freeText;
    }

    /**
     * This method is used for getting a classaname.
     * @return getFullClassName.
     */
    public final String getFullClassName() {

        return fullClassName;
    }

    /**
     * @return the methodName
     */
    public final String getMethodName() {

        return methodName;
    }

    /**
     * @return the otherObjects
     */
    public final Object getOtherObjects() {

        return otherObjects;
    }

    /**
     * @return the request
     */
    public final Serializable getRequest() {

        return request;
    }

    /**
     * @return the response
     */
    public final Serializable getResponse() {

        return response;
    }

    /**
     * @return the severity
     */
    public final int getSeverity() {

        return severity;
    }

    /**
     * @return the timeStamp
     */
    public final long getTimeStamp() {

        return timeStamp;
    }

    /**
     * @return the timeStamp formatted as String
     */
    public final String getTimeStampFormatted() {

        if (dtTimeStamp != null) { return logDtFormat.format(dtTimeStamp)
            .toUpperCase(); }
        return "TIMESTAMP";
    }

    /**
     * This method is used for returning timestamp.
     * @return
     */
    public final String getTimeTaken() {

        if (timeStampDiff > 0) { return "Duration: "
            + diffDtFormat.format(dtDiffTimeStamp); }
        return "";
    }

    /**
     * If you have Application Specific Data to be written to the log. Examples:
     * Record locator, Flight Number etc. If you do not set this explicitly -
     * the data from the Req Obj is used
     * @param applicationData applicationData
     */
    public final void setApplicationData(final Object applicationData) {

        this.applicationData = applicationData;
    }

    /**
     * @param className the className to set
     */
    public final void setClassName(final String className) {

        this.className = className;
    }

    /**
     * @param clientData the clientData to set.
     */
    public final void setClientData(final ClientData clientData) {

        this.clientData = clientData;
        if (request == null && clientData != null
            && clientData instanceof Serializable) {
            request = clientData;
        }
    }

    /**
     * @param codeLocation codeLocation.
     */
    public final void setCodeLocation(final String codeLocation) {

        this.codeLocation = codeLocation;
    }

    /**
     * @param exception the exception to set
     */
    public final void setException(final Throwable exception) {

        this.exception = exception;
    }

    /**
     * @param freeText the freeText to set
     */
    public final void setFreeText(final String freeText) {

        this.freeText = freeText;
        if (freeText == null && getException() != null) {
            this.freeText = getException().getMessage();
        }
    }

    public final void setFullClassName(final String fullClassName) {

        this.fullClassName = fullClassName;
    }

    /**
     * @param methodName the methodName to set
     */
    public final void setMethodName(final String methodName) {

        this.methodName = methodName;
    }

    /**
     * You can also provide this as a List.
     * @param pOtherObjects otherobjects
     */
    public final void setOtherObjects(final Object... pOtherObjects) {

        if (pOtherObjects != null) {
            otherObjects = Arrays.asList(pOtherObjects);
            for (int i = 0; i < otherObjects.size(); i++) {
                final Object theObj = otherObjects.get(i);
                if (!(theObj instanceof Serializable)) {
                    otherObjects.set(i, toXML(theObj));
                }
            }
        }
    }

    /**
     * @param request request
     */
    public final void setRequest(final Serializable request) {

        if (request != null && request instanceof ClientData) {
            setClientData((ClientData) request);
        } else {
            this.request = request;
        }
    }

    /**
     * @param response the response to set
     */
    public final void setResponse(final Serializable response) {

        this.response = response;
    }

    /**
     * @param severity the severity to set
     */
    public final void setSeverity(final int severity) {

        this.severity = severity;
    }
/**
 * 
 * This method is used to set a timestamp.
 */
    protected final void setTimeStamp() {

        timeStamp = java.util.Calendar.getInstance().getTimeInMillis();
        dtTimeStamp = new Date(timeStamp);
        // in case this class is GWT'd the above statement may have a problem
        // this.timeStamp = new java.util.Date().getTime();
    }

    @Override
    public final String toString() {

        return "\r\n"
            + "===================== Start Log Entry ====================="
            + "\r\n"
            + toStringLite()
            + "\r\n"
            + "Session Key\t: "
            + (getClientData() == null ? "" : getClientData()
                .getServerSessionKey())
            + "\r\n"
            + "Agent Id\t: "
            + (getClientData() == null ? "" : getClientData().getAgentId())
            + "\r\n"
            + "Agent City\t: "
            + (getClientData() == null ? "" : getClientData().getAgentCity())
            + "\r\n"
            + "Agt Device Id\t: "
            + (getClientData() == null ? "" : getClientData()
                .getAgentDeviceId())
            + "\r\n"
            + "Business Proc\t: "
            + (getClientData() == null ? "" : getClientData()
                .getBusinessProcess())
            + "\r\n"
            + "Web Session\t: "
            + (getClientData() == null ? "" : getClientData()
                .getClientSessionId())
            + "\r\n"
            + "Timestamp\t: "
            + getTimeStamp()
            + "\r\n"
            + "Class Name\t: "
            + getClassName()
            + "\r\n"
            + "Method Name\t: "
            + getMethodName()
            + "\r\n"
            + "Code Location\t: "
            + getCodeLocation()
            + "\r\n"
            + "Request	Obj\t: "
            + toXML(getRequest())
            + "\r\n"
            + "Response Obj\t: "
            + toXML(getResponse())
            + "\r\n"
            + "Other Objects\t: "
            + toXML(getOtherObjects())
            + "\r\n"
            + "Addl App Data\t: "
            + toXML(getApplicationData())
            + "\r\n"
            + "Exception\t: "
            + getException()
            + "\r\n"
            + "Free Text\t: "
            + getFreeText()
            + "\r\n"
            + "=====================  End Log Entry  =====================";
    }
/**
 * 
 * This method is used for padding text
 * @return column
 */
    public final String toStringLite() {

        String padding = "                                ";
        //final String col1 = getTimeStampFormatted();
        String col2 =
        	getCodeLocation() + " - " + getMethodName() + "()";
        String col4 = getTimeTaken();
        if (col4 != null && col4.trim().length() > 0) {
            padding = padding.replace(' ', '-');
        }
        // Some times getFreeText() having null Value.
        String col3 = "";
        if(getFreeText()!= null && !getFreeText().equals("")){
        	col3 = getFreeText();
        }
        if (col3!=null && col3.startsWith("ONSVCEXIT")) {
            col4 += "\n";
        }

        String retVal = col2 + " " + col3;
        
        if( col4 != null && !col4.isEmpty() )
        	retVal += " >" + col4; 

        return retVal;
    
    }
    
    public final String toStringReqRes() {

        String padding = "                                ";
        //final String col1 = getTimeStampFormatted();
        String col2 =
        	getCodeLocation() + " - " + getMethodName() + "() : " + getFreeText();
        String col4 = getTimeTaken();
        if (col4 != null && col4.trim().length() > 0) {
            padding = padding.replace(' ', '-');
        }
        // Some times getFreeText() having null Value.
        String col3 = "";
        Object beReq = getBackEndRequest();
        Object beRes = getBackEndResponse();
        
        if(beReq!= null && beReq.toString()!=null && !beReq.toString().isEmpty() ){
        	col3 = beReq.toString() + "\n";
        }
        if(beRes!= null && beRes.toString()!=null && !beRes.toString().isEmpty() ){
        	col3 = beRes.toString() + "\n";
        }
        
        String retVal = col2 + " " + col4;
        
        if( col3 != null && !col3.isEmpty() )
        	retVal += " \n>" + col3; 

        return retVal;
    }
    
    
    public static void addCommonExceptionClsName(String pClassName){
    	commonExClsName.add(pClassName);
    }
    
    /**
     * Getters and Setters for the fields 
     * having one to one mapping with the database.
     */
    
public String getUserPermission() {
		
		return userPermission;
	}
	
	public void setUserPermission(String userPermission) {
		this.userPermission = userPermission;
	}


	public String getLogEntryTypeCode() {
		return logEntryTypeCode;
	}
	
	public void setLogEntryTypeCode(String logEntryTypeCode) {
		this.logEntryTypeCode = logEntryTypeCode;
	}

	public long getErrorMsgNo() {
		return errorMsgNo;
	}
	
	public void setErrorMsgNo(long errorMsgNo) {
		this.errorMsgNo = errorMsgNo;
	}

	public String getErrorMessage() {
		return errorMessage;
	}
	
	public void setErrorMessage(String errorMessage) {
		this.errorMessage = errorMessage;
	}

	public String getBkndInterfaceTypCode() {
		
		return bkndInterfaceTypCode;
	}
	public void setBkndInterfaceTypCode(String bkndInterfaceTypCode) {
		this.bkndInterfaceTypCode = bkndInterfaceTypCode;
	}

	public String getBkndEnvironmentName() {
		return bkndEnvironmentName;
	}
	public void setBkndEnvironmentName(String bkndEnvironmentName) {
		this.bkndEnvironmentName = bkndEnvironmentName;
	}

	public String getBkndServiceName() {
		return bkndServiceName;
	}
	
	public void setBkndServiceName(String bkndServiceName) {
		this.bkndServiceName = bkndServiceName;
	}

	public String getBkndSessionIdentifier() {
		return bkndSessionIdentifier;
	}
	
	public void setBkndSessionIdentifier(String bkndSessionIdentifier) {
		this.bkndSessionIdentifier = bkndSessionIdentifier;
	}

	public String getBkndActionName() {
		return bkndActionName;
	}
	
	public void setBkndActionName(String bkndActionName) {
		this.bkndActionName = bkndActionName;
	}

	public String getBkndLocIdentifier() {
		return bkndLocIdentifier;
	}
	
	public void setBkndLocIdentifier(String bkndLocIdentifier) {
		this.bkndLocIdentifier = bkndLocIdentifier;
	}

	public long getBkngCreationDate() {
		return bkngCreationDate;
	}

	public void setBkngCreationDate(long bkngCreationDate) {
		this.bkngCreationDate = bkngCreationDate;
	}

	public String getTktDocNo() {
		
		return tktDocNo;
	}
	
	public void setTktDocNo(String tktDocNo) {
		this.tktDocNo = tktDocNo;
	}

	public long getTktDocIssueLocalDate() {
		return tktDocIssueLocalDate;
	}
	
	public void setTktDocIssueLocalDate(long tktDocIssueLocalDate) {
		this.tktDocIssueLocalDate = tktDocIssueLocalDate;
	}

	public String getMktCarrierCode() {
		return mktCarrierCode;
	}

	public void setMktCarrierCode(String mktCarrierCode) {
		this.mktCarrierCode = mktCarrierCode;
	}

	public String getMktFlightNo() {
		return mktFlightNo;
	}
	
	public void setMktFlightNo(String mktFlightNo) {
		this.mktFlightNo = mktFlightNo;
	}

	public String getBkngClassOfServiceCode() {
		
		return bkngClassOfServiceCode;
	}
	
	public void setBkngClassOfServiceCode(String bkngClassOfServiceCode) {
		this.bkngClassOfServiceCode = bkngClassOfServiceCode;
	}

	public String getOpAsCarrierCode() {
		return opAsCarrierCode;
	}
	
	public void setOpAsCarrierCode(String opAsCarrierCode) {
		this.opAsCarrierCode = opAsCarrierCode;
	}

	public String getOpAsFlightNumber() {
		return opAsFlightNumber;
	}
	
	public void setOpAsFlightNumber(String opAsFlightNumber) {
		this.opAsFlightNumber = opAsFlightNumber;
	}

	public long getSchldSegDeptlocalDate() {
		return schldSegDeptlocalDate;
	}
	
	public void setSchldSegDeptlocalDate(long schldSegDeptlocalDate) {
		this.schldSegDeptlocalDate = schldSegDeptlocalDate;
	}
	
	public String getOrgTrnsprtStnCode() {
		return orgTrnsprtStnCode;
	}
	
	public void setOrgTrnsprtStnCode(String orgTrnsprtStnCode) {
		this.orgTrnsprtStnCode = orgTrnsprtStnCode;
	}
	
	public String getDestTrnsprtStnCode() {
		return destTrnsprtStnCode;
	}
	
	public void setDestTrnsprtStnCode(String destTrnsprtStnCode) {
		this.destTrnsprtStnCode = destTrnsprtStnCode;
	}

	public String getCustIdentifier() {
		return custIdentifier;
	}
	
	public void setCustIdentifier(String custIdentifier) {
		this.custIdentifier = custIdentifier;
	}

	public String getMbrPrgTierCode() {
		return mbrPrgTierCode;
	}
	public void setMbrPrgTierCode(String mbrPrgTierCode) {
		this.mbrPrgTierCode = mbrPrgTierCode;
	}

	public String getCustTypeCode() {
		return custTypeCode;
	}
	
	public void setCustTypeCode(String custTypeCode) {
		this.custTypeCode = custTypeCode;
	}

	public String getAppClientIdentifier() {
		return appClientIdentifier;
	}
	
	public void setAppClientIdentifier(String appClientIdentifier) {
		this.appClientIdentifier = appClientIdentifier;
	}

	public String getClientAppDetail() {
		return clientAppDetail;
	}
	public void setClientAppDetail(String clientAppDetail) {
		this.clientAppDetail = clientAppDetail;
	}

	public Object getBackEndRequest() {
		return backEndRequest;
	}
	
	public void setBackEndRequest(Object backEndRequest) {
		this.backEndRequest = backEndRequest;
	}

	public Object getBackEndResponse() {
		return backEndResponse;
	}
	
	public void setBackEndResponse(Object backEndResponse) {
		this.backEndResponse = backEndResponse;
	}

	public Object getAddtionalObjects() {
		return addtionalObjects;
	}

	public void setAddtionalObjects(Object addtionalObjects) {
		this.addtionalObjects = addtionalObjects;
	}

	public Object getUserSettings() {
		return userSettings;
	}
	
	public void setUserSettings(Object userSettings) {
		this.userSettings = userSettings;
	}

	public Object getApplicationSettings() {
		
		return applicationSettings;
	}
	
	public void setApplicationSettings(Object applicationSettings) {
		this.applicationSettings = applicationSettings;
	}

	public boolean isLogged() {
		return logged;
	}

	void setLogged(boolean logged) {
		this.logged = logged;
	}
}
